#include "lvgl/lvgl.h"
#include "lv_drivers/display/fbdev.h"
#include "lv_examples/lv_examples.h"
#include "lv_drivers/indev/evdev.h"
#include "lvgl/src/hal/lv_hal_indev.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include "stdio.h"
#include "stdint.h"
#include "main.h"
#include "serial.h"
#include "autorun.h"
#include "math.h"
#include "stdlib.h"

#define DISP_BUF_SIZE (LV_VER_RES_MAX * LV_HOR_RES_MAX)
device_t device;

uint32_t custom_tick_get(void);
void slave485_receive_handler(byte *data, int len);
void disp_data_refresh(lv_timer_t *timer);
void modbus_send_frame(byte *data, int len);
void modbus_sync_reg_task(void *arg);

void slave485_register_init(void);
void esp32_read_vaccum_cb(void);
void esp32_read_callback(void);

void slave485_register_init()
{
    // mpump status coil
    slave_coil_t coil = {
        .addr = 0x0010,
        .size = 8};
    slave485_add_coil(&device.slave485, &coil);
    // test reg
    slave_reg_t reg = {
        .addr = 0x0000,
        .size = 7,
        .reg_cb = NULL};
    slave485_add_reg(&device.slave485, &reg);

    // mpump reg
    // 06 start:0010 0003 | end:0010 0008 | high speed:0010 0001 | low speed:0010 0002
    // 03 read_data:0012 0004(0012 voltage , 0013 current, 0014 r_speed, 0015 tempture)
    // 01 read_status: 0010 - 0017
    reg.addr = 0x0010;
    reg.size = 6;
    reg.reg_cb = NULL;
    slave485_add_reg(&device.slave485, &reg);

    // jpump reg
    // 06 start:0060 0001 | end:0060 0005 | fault reset:0060 0007
    // 03 read_status:0061 0001(recive: 0001 run, 0005 standby, 0006 fault)
    // 03 read_data: 0062 0005(0062 voltage, 0063 current, 0064 r_speed, 0065 tempture1, 0066 tempture2)
    reg.addr = 0x0060;
    reg.size = 7;
    reg.reg_cb = NULL;
    slave485_add_reg(&device.slave485, &reg);

    // vacuum
    // 03 read_data: 0020 0002(ASCII)
    reg.addr = 0x0020;
    reg.size = 2;
    reg.reg_cb = NULL;
    slave485_add_reg(&device.slave485, &reg);
}
void slave485_receive_handler(byte *data, int len)
{
    // printf("\nslave485_receive_handler\n");
    // printf("recive data:");
    // for (int i = 0; i < len; i++)
    // {
    //     printf("%02x ", data[i]);
    // }
    // printf("\n");
    slave485_err_t slave_err;
    uint16_t coil_addr = (data[2] << 8) | data[3];
    uint16_t reg_addr = (data[2] << 8) | data[3];
    uint16_t *buf;
    byte *buf_b;
    if (data[0] == 0x05)
    {
        if (data[1] == 0x03)
        {
            // printf("read reg\n");
            uint16_t reg_count = ((uint16_t)data[4] << 8) | data[5];
            buf = malloc(sizeof(uint16_t) * reg_count + 5);
            buf_b = (byte *)buf;
            buf_b[0] = data[0];
            buf_b[1] = 0x03;
            buf_b[2] = (reg_count * 2) & 0xFF;
            slave_err = slave485_read_reg(&device.slave485, reg_addr, (uint16_t *)(buf_b + 3), reg_count);
            // change byte order
            byte *buf_change = buf_b + 3;
            for (int i = 0; i < reg_count; i++)
            {
                byte temp = buf_change[2 * i];
                buf_change[2 * i] = buf_change[2 * i + 1];
                buf_change[2 * i + 1] = temp;
            }
            uint16_t crc = pf_crc16(buf_b, reg_count * 2 + 3, 0x8005);
            buf_b[3 + reg_count * 2] = crc & 0xFF;
            buf_b[4 + reg_count * 2] = crc >> 8;
            // printf("slave485_err(read):%d\n", slave_err);
            // printf("send data size:%d\n", sizeof(uint16_t) * reg_count + 5);
            // for (int i = 0; i < reg_count * 2 + 5; i++)
            // {
            //     printf("%02x ", buf_b[i]);
            // }
            // printf("\n");
            serial_write(&device.rs485, buf_b, sizeof(uint16_t) * reg_count + 5);
            free(buf);
        }
        else if (data[1] == 0x01) // read coil...
        {
            printf("read coil\n");
            uint16_t coil_count = ((uint16_t)data[4] << 8) | data[5];
            int byte_count;
            if (coil_count % 8 == 0)
                byte_count = coil_count / 8;
            else
                byte_count = coil_count / 8 + 1;

            // printf("byte count:%d\n", byte_count);
            // buf = malloc(byte_count + 5);
            buf_b = malloc(byte_count + 5);
            // buf_b = (byte *)buf;
            buf_b[0] = data[0];
            buf_b[1] = 0x01;
            buf_b[2] = byte_count;
            slave_err = slave485_read_coil(&device.slave485, coil_addr, buf_b + 3, coil_count);
            printf("slave485_err(write):%d\n", slave_err);
            uint16_t crc = pf_crc16(buf_b, byte_count + 3, 0x8005);
            buf_b[3 + byte_count] = crc & 0xFF;
            buf_b[4 + byte_count] = crc >> 8;
            serial_write(&device.rs485, buf_b, byte_count + 5);
            free(buf_b);
        }
        else if (data[1] == 0x05)
        {
            uint16_t reg_value = ((uint16_t)data[4] << 8) | data[5];
            slave_err = slave485_write_coil(&device.slave485, coil_addr, reg_value);
            // printf("slave485_err(write):%d\n", slave_err);
            serial_write(&device.rs485, data, len);
        }
        else if (data[1] == 0x06)
        {
            uint16_t reg_value = ((uint16_t)data[4] << 8) | data[5];
            slave_err = slave485_write_reg(&device.slave485, reg_addr, reg_value);
            // printf("slave485_err(write):%d\n", slave_err);
            serial_write(&device.rs485, data, len);
        }
    }
}

void disp_data_refresh(lv_timer_t *timer)
{
    if (lobj[LID_DEV_BTN_DEVICE_CTL] != NULL && device.current_page == PAGE_DEVICE)
    {
        lv_obj_t *label = lv_obj_get_child(lobj[LID_DEV_BTN_DEVICE_CTL], 0);
        if (device.autorun.start)
            lv_label_set_text(label, "停止");
        else
            lv_label_set_text(label, "启动");
    }
    if (lobj[LID_BAR_TIME] != NULL)
    {
        time_t rawtime = time(NULL);
        struct tm *Cur_time = localtime(&rawtime);
        static char Time[64];
        strftime(Time, sizeof(Time), "%Y-%m-%d    %H:%M:%S", Cur_time);
        lv_label_set_text(lobj[LID_BAR_TIME], Time);
    }
    // mpump info
    if (device.current_page == PAGE_DEVICE && lobj[LID_DEV_MPM_RS] != NULL && lobj[LID_DEV_MPM_CURRENT] != NULL && lobj[LID_DEV_MPM_VOLTAGE] != NULL && lobj[LID_DEV_MPM_TEMPERATURE] != NULL)
    {
        uint16_t mpump_data[6] = {0, 0, 0, 0, 0, 0};
        slave485_read_reg(&device.slave485, MPUMP_FIRST_ADR, mpump_data, 6);
        char str[16];
        sprintf(str, "%d.%d V", mpump_data[2] / 100, mpump_data[2] % 100 / 10);
        lv_label_set_text(lobj[LID_DEV_MPM_VOLTAGE], str);
        sprintf(str, "%d.%d A", mpump_data[3] / 100, mpump_data[3] % 100 / 10);
        lv_label_set_text(lobj[LID_DEV_MPM_CURRENT], str);
        sprintf(str, "%d RPM", mpump_data[4] * 60);
        lv_label_set_text(lobj[LID_DEV_MPM_RS], str);
        sprintf(str, "%d ℃", mpump_data[5]);
        lv_label_set_text(lobj[LID_DEV_MPM_TEMPERATURE], str);
    }
    // mpump status
    if (device.current_page == PAGE_DEVICE && lobj[LID_DEV_RADIO_MPUMP] && lobj[LID_DEV_RADIO_MPM_HIGH_SPEED] != NULL && lobj[LID_DEV_RADIO_MPM_NORMAL_SPEED] != NULL && lobj[LID_DEV_RADIO_MPM_FAIL_START] != NULL && lobj[LID_DEV_RADIO_MPM_OVERHEAT] != NULL && lobj[LID_DEV_RADIO_MPM_OVERCURRENT] != NULL && lobj[LID_DEV_RADIO_MPM_WORKING] != NULL)
    {
        uint8_t mpump_status_b = 0;
        bool mpump_status[8] = {0, 0, 0, 0, 0, 0, 0, 0};
        slave485_read_coil(&device.slave485, 0x10, &mpump_status_b, 8);
        for (int i = 0; i < 8; i++)
        {
            mpump_status[i] = (mpump_status_b >> i) & 1;
            if (mpump_status[i] == 0)
            {
                lv_obj_set_style_bg_color(lobj[14 + i], lv_color_hex(0x9E9E9E), 0);
                if (i == 4)
                    lv_obj_set_style_bg_color(lobj[LID_DEV_RADIO_MPUMP], lv_color_hex(0x9E9E9E), 0);
            }
            if (mpump_status[i] == 1)
            {
                if (i == 2 || i == 3)
                    lv_obj_set_style_bg_color(lobj[14 + i], lv_color_hex(0x009E00), 0);
                if (i == 4)
                {
                    lv_obj_set_style_bg_color(lobj[14 + i], lv_color_hex(0x009E00), 0);
                    lv_obj_set_style_bg_color(lobj[LID_DEV_RADIO_MPUMP], lv_color_hex(0x009E00), 0);
                }
                if (i == 0 || i == 1 || i == 5 || i == 6)
                    lv_obj_set_style_bg_color(lobj[14 + i], lv_color_hex(0x9E0000), 0);
            }
        }
    }
    // jmpump info & status
    if (device.current_page == PAGE_DEVICE && lobj[LID_DEV_RADIO_JPUMP] && lobj[LID_DEV_RADIO_JPM_WORKING] && lobj[LID_DEV_RADIO_JPM_FAULT] && lobj[LID_DEV_JPM_RS] != NULL && lobj[LID_DEV_JPM_CURRENT] != NULL && lobj[LID_DEV_JPM_VOLTAGE] != NULL && lobj[LID_DEV_JPM_TEMPERATURE1] != NULL && lobj[LID_DEV_JPM_TEMPERATURE2] != NULL)
    {
        uint16_t jpump_data[7] = {0, 0, 0, 0, 0, 0, 0}; // 2-jpump_voltage_value 3-jpump_current_value 4-jpump_rotational_speed_value 5-jpump_temperature_value1 6-jpump_temperature_value2
        slave485_read_reg(&device.slave485, JPUMP_FIRST_ADE, jpump_data, 7);
        char str[16];
        sprintf(str, "%d.%d V", jpump_data[2] / 10, jpump_data[2] % 10);
        lv_label_set_text(lobj[LID_DEV_JPM_VOLTAGE], str);
        sprintf(str, "%d.%d A", jpump_data[3] / 10, jpump_data[3] % 10);
        lv_label_set_text(lobj[LID_DEV_JPM_CURRENT], str);
        sprintf(str, "%d RPM", jpump_data[4]);
        lv_label_set_text(lobj[LID_DEV_JPM_RS], str);
        sprintf(str, "%d.%d ℃", jpump_data[5] / 10, jpump_data[5] % 10);
        lv_label_set_text(lobj[LID_DEV_JPM_TEMPERATURE1], str);
        sprintf(str, "%d.%d ℃", jpump_data[6] / 10, jpump_data[6] % 10);
        lv_label_set_text(lobj[LID_DEV_JPM_TEMPERATURE2], str);
        if (jpump_data[1] == 0x0005)
        {
            lv_obj_set_style_bg_color(lobj[LID_DEV_RADIO_JPM_WORKING], lv_color_hex(0x9E9E9E), 0);
            lv_obj_set_style_bg_color(lobj[LID_DEV_RADIO_JPUMP], lv_color_hex(0x9E9E9E), 0);
        }
        else if (jpump_data[1] == 0x0002)
            lv_obj_set_style_bg_color(lobj[LID_DEV_RADIO_JPM_WORKING], lv_color_hex(0x009E00), 0);
        else if (jpump_data[1] == 0x0006)
            lv_obj_set_style_bg_color(lobj[LID_DEV_RADIO_JPM_FAULT], lv_color_hex(0x9E0000), 0);
    }
    // left list
    if (device.current_page == PAGE_DEVICE && lobj[LID_DEV_RADIO_VALVE_MAIN] != NULL)
    {
        uint8_t valve_main_status;
        slave485_read_coil(&device.slave485, 0x0000, &valve_main_status, 8);
        valve_main_status = valve_main_status >> 2 & 1;
        if (valve_main_status == 0)
            lv_obj_set_style_bg_color(lobj[LID_DEV_RADIO_VALVE_MAIN], lv_color_hex(0x9E9E9E), 0);
        else if (valve_main_status == 1)
            lv_obj_set_style_bg_color(lobj[LID_DEV_RADIO_VALVE_MAIN], lv_color_hex(0x009E00), 0);

        // slave485_read_reg(&device.slave485, 0x0061, &mpump_status, 1); mpump read coil
    }
    if (device.current_page == PAGE_DEVICE && lobj[LID_DEV_VACUUM_LABEL] != NULL)
    {
        char str[16];
        uint16_t vacuum_value[2];
        slave485_read_reg(&device.slave485, VACUUM_FIRST_ADR, vacuum_value, 2);
        double value = ((vacuum_value[0] >> 8) - '0');
        value += (double)((vacuum_value[0] & 0xFF) - '0') * 0.1;
        int powx = (vacuum_value[1] & 0xFF) - '0';
        if ((vacuum_value[1] >> 8) == '-')
        {
            powx = -powx;
        }
        value = value * pow(10, powx);
        sprintf(str, "%.1E Pa", value);
        lv_label_set_text(lobj[LID_DEV_VACUUM_LABEL], str);
    }
}

void esp32_read_callback(void)
{
    esp32_analyze(&device.esp32);
}

__attribute__((weak)) int main(void)
{
    /*LittlevGL init*/
    lv_init();

    /*Linux frame buffer device init*/
    fbdev_init();

    /*A small buffer for LittlevGL to draw the screen's content*/
    static lv_color_t buf[DISP_BUF_SIZE];
    static lv_color_t buf1x[DISP_BUF_SIZE];
    /*Initialize a descriptor for the buffer*/
    static lv_disp_draw_buf_t disp_buf;
    lv_disp_draw_buf_init(&disp_buf, buf, buf1x, DISP_BUF_SIZE);

    /*Initialize and register a display driver*/
    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.draw_buf = &disp_buf;
    disp_drv.flush_cb = fbdev_flush;
    disp_drv.hor_res = 1024;
    disp_drv.ver_res = 600;
    lv_disp_drv_register(&disp_drv);

    lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv); /*Basic initialization*/
    evdev_init();
    indev_drv.type = LV_INDEV_TYPE_POINTER; /*See below.*/
    indev_drv.read_cb = evdev_read;         /*See below.*/
    lv_indev_drv_register(&indev_drv);      /*Register the driver in LittlevGL*/

    // system initlize
    {
        memset(&device, 0, sizeof(device_t));
    }
    vaacum_init(&device.vaacum, 0x07);
    esp32_init(&device.esp32, ESP32_ADDR);

    ui_init(&device.ui);
    ui_start();

    // clock_t start_time = clock();
    start_time = time(NULL);

    serial_config_t config;
    config.baud = 9600;
    config.port = "/dev/ttyS2";
    config.parity = SERIAL_PARITY_NONE;
    config.flow_control = SERIAL_FLOW_CONTROL_NONE;
    config.data_bits = SERIAL_DATA_BITS_8;
    config.stop_bits = SERIAL_STOP_BITS_1;
    config.read_timeout = 50;
    config.rxbuffer_size = 1024;
    serial_init(&device.rs485, &config);
    serial_open(&device.rs485);
    serial_receive_handler_register(&device.rs485, slave485_receive_handler);
    // slave485
    slave485_init(&device.slave485, 0x05, 9600);
    slave485_register_init();

    // uint16_t test_data[3] = {0x010A, 0x010B, 0x010C};
    // slave485_write_reg(&device.slave485, 0x0001, test_data);
    // slave485_write_reg(&device.slave485, 0x0002, test_data + 1);
    // uint16_t test_true = 0xFF00;
    // uint8_t data_read[2];
    // slave485_write_coil(&device.slave485, 0x0010, &test_true);
    // slave485_write_coil(&device.slave485, 0x0018, &test_true);
    // slave485_write_coil(&device.slave485, 0x0011, &test_true);
    // slave485_write_coil(&device.slave485, 0x0012, &test_true);
    // slave485_read_coil(&device.slave485, 0x0010, data_read, 9);
    // printf("read_coil:[%02X],[%02x]\n", data_read[0], data_read[1]);
    pthread_create(&device.autorun.thread, NULL, (void *)autorun_task, NULL);
    lv_timer_create(disp_data_refresh, 200, NULL);
    /*
    //modbus

    // modbus_err_t err = md_init(&device.modbus, modbus_send_frame);
    // // add vaccum meter
    // err = md_dev_add(&device.modbus, VACCUM_ADDR, 64, 64);
    // err = md_dev_set_timeout(&device.modbus, VACCUM_ADDR, 100);

    // add mpump
    err = md_dev_add(&device.modbus, MOLEPUMP_ADDR, 0x1001, 0x1000);
    err = md_dev_set_timeout(&device.modbus, MOLEPUMP_ADDR, 200);

    // add esp32
    err = md_dev_add(&device.modbus, ESP32_ADDR, 256, 0);
    err = md_dev_set_timeout(&device.modbus, ESP32_ADDR, 200);

    printf("add device err:%d\n", err);

    md_reg_sync_list_init(&device.modbus);
    md_write_cmd_list_init(&device.modbus);
    md_reg_sync_list_add(&device.modbus, MDCMD_READ_REGS, ESP32_ADDR, ESP32_REG_ADR_DEVICE_STATUS, 1, 0, 10, esp32_read_callback);
    md_reg_sync_list_add(&device.modbus, MDCMD_READ_REGS, ESP32_ADDR, ESP32_REG_ADR_VACUUM, 2, 0, 10, esp32_read_vaccum_cb);
    md_reg_sync_list_add(&device.modbus, MDCMD_READ_REGS, MOLEPUMP_ADDR, MPUMP_REG_ADR_STATUS & 0xFFFF, (MPUMP_REG_ADR_STATUS >> 16) & 0xFF, 0, 300, NULL);
    md_reg_sync_list_add(&device.modbus, MDCMD_READ_REGS, MOLEPUMP_ADDR, MPUMP_REG_ADR_TEMP & 0xFFFF, (MPUMP_REG_ADR_TEMP >> 16) & 0xFF, 0, 10, NULL);

    // md_reg_sync_list_add(&device.modbus, MDCMD_READ_REGS, VACCUM_ADDR, VACCUM_REG_ADR_VALUE & 0xFFFF, (VACCUM_REG_ADR_VALUE >> 16) & 0xFF, 0, 10, vaacum_read_callback);
    printf("start sync\n");
    pthread_create(&device.rs485.thread, NULL, (void *)modbus_sync_reg_task, NULL);

    // Open molecular pump power
    if (device.esp32.device_status_d[6] == 0)
    {
        modbus_err_t errz = md_reg_sync_list_add(&device.modbus, MDCMD_WRITE_REG, ESP32_ADDR, ESP32_REG_ADR_DEVICE_STATUS, 0, device.esp32.device_status_b |= 0x0040, 300, NULL);
        if (errz != MDERR_OK)
            printf("err:%d", errz);
    }
    */
    while (1)
    {
        lv_task_handler();
        usleep(5 * 1000);
    }
    return 0;
}

/*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/
uint32_t custom_tick_get(void)
{
    static uint64_t start_ms = 0;
    if (start_ms == 0)
    {
        struct timeval tv_start;
        gettimeofday(&tv_start, NULL);
        start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
    }

    struct timeval tv_now;
    gettimeofday(&tv_now, NULL);
    uint64_t now_ms;
    now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;

    uint32_t time_ms = now_ms - start_ms;
    return time_ms;
}
